package com.jichangxiu.common.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HTMLFilter;
import com.jichangxiu.common.cache.RedisService;
import com.jichangxiu.common.constant.RedisKeys;
import com.jichangxiu.common.entity.bo.Record;
import com.jichangxiu.common.exception.FrameworkException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * StrUtils:
 *
 * @author Jcx
 * @create 2023-04-03 21:49
 */
@Component
public class StrUtils extends StrUtil {

    private final static String FORMAT_CODE = "00000";

    private static final ThreadLocal<HTMLFilter> HTML_FILTER = ThreadLocal.withInitial(() -> {
        HTMLFilter htmlFilter = new HTMLFilter();
        // 避免 " 被转成 &quot; 字符
        ReflectUtil.setFieldValue(htmlFilter, "encodeQuotes", false);
        return htmlFilter;
    });

    private static final Pattern NUMBER_PATTERN = Pattern.compile("-?[0-9]+(\\[0-9]+)?");

    @Resource
    private RedisService redisService;

    public synchronized String createNo(String type) {
        String redisKey = RedisKeys.createNo(type, DateUtil.format(new Date(), "yyyyMMdd"));
        Long count = redisService.increment(redisKey, 1);
        redisService.expire(redisKey, 1, TimeUnit.DAYS);
        String code = new DecimalFormat(FORMAT_CODE).format(count);
        return type.toUpperCase() + "_" + DateUtil.format(new Date(), "yyyyMMdd") + "_" + code;
    }

    /**
     * 将指定单词首字母小写
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordFirstSmall(String str) {
        return str.substring(0, 1).toLowerCase() + str.substring(1);
    }

    /**
     * 将指定单词首字母大写
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordFirstBig(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    /**
     * 指定字符串的字符串下划线转大写模式
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordEachBig(String str) {
        StringBuilder newStr = new StringBuilder();
        for (String s : str.split("_")) {
            newStr.append(wordFirstBig(s));
        }
        return newStr.toString();
    }

    /**
     * 返回下划线转小驼峰形式
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordEachBigFs(String str) {
        return wordFirstSmall(wordEachBig(str));
    }

    /**
     * 驼峰转下划线
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordHumpToLine(String str) {
        return str.replaceAll("[A-Z]", "_$0").toLowerCase();
    }

    /**
     * 下划线转中划线
     *
     * @param str 传入字符串
     * @return 转换后的字符串
     */
    public static String wordEachKebabCase(String str) {
        return str.replaceAll("_", "-");
    }

    /**
     * XSS过滤
     *
     * @param content 需要过滤的内容
     * @return 返回过滤后的内容
     */
    public static String filter(String content) {
        return HTML_FILTER.get().filter(content);
    }

    /**
     * 对象转换为集合
     *
     * @param obj   对象
     * @param clazz 类型
     * @param <R>   泛型
     * @return 集合
     */
    private static <R> List<R> objCastList(Object obj, Class<R> clazz) {
        List<R> list = new ArrayList<>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                list.add(clazz.cast(o));
            }
        }
        return list;
    }

    /**
     * 利用反射通过 get 方法获取 bean 中字段 fieldName 的值
     *
     * @param bean      对象
     * @param fieldName 属性名
     * @return 属性值
     */
    private static Object getFieldValue(Object bean, String fieldName) {
        try {
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Object rObject = null;
            Method method = null;
            @SuppressWarnings("rawtypes") Class[] classArr = new Class[0];
            method = bean.getClass().getMethod(methodName, classArr);
            rObject = method.invoke(bean);
            return rObject;
        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw FrameworkException.build("反射获取对象属性名失败", e);
        }
    }

    /**
     * 利用发射调用 bean.set 方法将 value 设置到字段
     *
     * @param bean      对象
     * @param fieldName 属性名
     * @param value     属性值
     */
    private static void setFieldValue(Object bean, String fieldName, Object value) {
        try {
            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            //利用反射调用bean.set方法将value设置到字段
            @SuppressWarnings("rawtypes") Class[] classArr = new Class[1];
            classArr[0] = "java.lang.String".getClass();
            Method method = bean.getClass().getMethod(methodName, classArr);
            method.invoke(bean, value);
        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw FrameworkException.build("反射设置对象属性失败", e);
        }
    }

    /**
     * 去掉 bean 中所有属性为字符串的前后空格
     *
     * @param bean 对象
     */
    public static void trimBeanStr(Object bean) {
        if (ObjectUtil.isNotEmpty(bean)) {
            // 获取所有的字段包括public,private,protected,private
            Field[] fields = bean.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field f = fields[i];
                // 对其中的集合同样进行空格处理
                if (f.getType().getName().equals("java.util.List")) {
                    List<Object> objects = objCastList(getFieldValue(bean, f.getName()), Object.class);
                    for (Object object : objects) {
                        trimBeanStr(object);
                    }
                }
                if (f.getType().getName().equals("java.lang.String")) {
                    String key = f.getName();
                    //获取字段名
                    Object value = getFieldValue(bean, key);
                    if (value == null) {
                        continue;
                    }
                    setFieldValue(bean, key, value.toString().trim());
                }
            }
        }
    }

    /**
     * 校验 String 是否全是中文
     *
     * @param name 被校验的字符串
     * @return true 代表全是汉字
     */
    public static boolean checkNameChinese(String name) {
        boolean res = true;
        char[] cTemp = name.toCharArray();
        for (int i = 0; i < name.length(); i++) {
            if (!isRightChar(cTemp[i])) {
                res = false;
                break;
            }
        }
        return res;
    }

    /**
     * 验证是否是汉字 或 者0-9、a-z、A-Z
     *
     * @param c 被验证的char
     * @return true代表符合条件
     */
    public static boolean isRightChar(char c) {
        return isChinese(c) || isWord(c);
    }

    /**
     * 校验某个字符是否是a-z、A-Z、_、0-9
     *
     * @param c 被校验的字符
     * @return true 代表符合条件
     */
    public static boolean isWord(char c) {
        String regEx = "[\\w]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher("" + c);
        return m.matches();
    }

    /**
     * 判定输入的是否是汉字
     *
     * @param c 被校验的字符
     * @return true 代表是汉字
     */
    public static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
            return true;
        }
        return false;
    }

    /**
     * 判断字符串中是否包含中文
     * <p>
     * 不能校验是否为中文标点符号
     *
     * @param str 待校验字符串
     * @return 是否为中文
     */
    public static boolean isContainChinese(String str) {
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
        Matcher m = p.matcher(str);
        if (m.find()) {
            return true;
        }
        return false;
    }

    /**
     * 过滤掉中文
     *
     * @param str 待过滤中文的字符串
     * @return 过滤掉中文后字符串
     */
    public static String filterChinese(String str) {
        // 用于返回结果
        String result = str;
        boolean flag = isContainChinese(str);
        if (flag) {// 包含中文
            // 用于拼接过滤中文后的字符
            StringBuffer sb = new StringBuffer();
            // 用于校验是否为中文
            boolean flag2 = false;
            // 用于临时存储单字符
            char chinese = 0;
            // 5.去除掉文件名中的中文
            // 将字符串转换成char[]
            char[] charArray = str.toCharArray();
            // 过滤到中文及中文字符
            for (int i = 0; i < charArray.length; i++) {
                chinese = charArray[i];
                flag2 = isChinese(chinese);
                if (!flag2) {// 不是中日韩文字及标点符号
                    sb.append(chinese);
                }
            }
            result = sb.toString();
        }
        return result;
    }

    /**
     * 过滤掉 中文 以及 a-z、A-Z、_、0-9
     *
     * @param str 待过滤中文的字符串
     * @return 过滤掉中文后字符串
     */
    public static String filterChineseAndWord(String str) {
        // 用于返回结果
        String result = str;
        boolean flag = isContainChinese(str);
        if (flag) {// 包含中文
            // 用于拼接过滤中文后的字符
            StringBuffer sb = new StringBuffer();
            // 用于校验是否为中文
            boolean flag2 = false;
            // 用于校验是否为字母以及数字
            boolean flag3 = false;
            // 用于临时存储单字符
            char chinese = 0;
            // 5.去除掉文件名中的中文
            // 将字符串转换成char[]
            char[] charArray = str.toCharArray();
            // 过滤到中文及中文字符
            for (int i = 0; i < charArray.length; i++) {
                chinese = charArray[i];
                flag2 = isChinese(chinese);
                flag3 = isWord(chinese);
                if (!flag2 && !flag3) {// 不是中日韩文字及标点符号
                    sb.append(chinese);
                }
            }
            result = sb.toString();
        }
        return result;
    }

    /**
     * 校验一个字符是否是汉字
     *
     * @param c 被校验的字符
     * @return true代表是汉字
     */
    public static boolean isChineseChar(char c) {
        try {
            return String.valueOf(c).getBytes(StandardCharsets.UTF_8).length > 1;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 验证字符串内容是否包含下列非法字符<br>
     * `~!#%^&*=+\\|{};:'\",<>/?○●★☆☉♀♂※¤╬の〆
     *
     * @param content 字符串内容
     * @return 't'代表不包含非法字符，otherwise代表包含非法字符。
     */
    public static char validateLegalString(String content) {
        String illegal = "`~!#%^&*=+\\|{};:'\",<>/?○●★☆☉♀♂※¤╬の〆";
        char isLegalChar = 't';
        L1:
        for (int i = 0; i < content.length(); i++) {
            for (int j = 0; j < illegal.length(); j++) {
                if (content.charAt(i) == illegal.charAt(j)) {
                    isLegalChar = content.charAt(i);
                    break L1;
                }
            }
        }
        return isLegalChar;
    }


    /**
     * 校验 String 是否全是中文
     *
     * @param name 被校验的字符串
     * @return true代表全是汉字
     */
    public static boolean checkNameIsWord(String name) {
        boolean res = true;
        char[] cTemp = name.toCharArray();
        for (int i = 0; i < name.length(); i++) {
            if (!isWord(cTemp[i])) {
                res = false;
                break;
            }
        }
        return res;
    }

    /**
     * 判断字符串是否全为英文
     *
     * @param str 被校验的字符串
     * @return true代表全是英文
     */
    public void judge(String str) {
        //【全为英文】返回true  否则false
        boolean result1 = str.matches("[a-zA-Z]+");
        //【全为数字】返回true
        boolean result6 = str.matches("[0-9]+");
        //【除英文和数字外无其他字符(只有英文数字的字符串)】返回true 否则false
        boolean result2 = str.matches("[a-zA-Z0-9]+");
        //【含有英文】true
        String regex1 = ".*[a-zA-z].*";
        boolean result3 = str.matches(regex1);
        //【含有数字】true
        String regex2 = ".*[0-9].*";
        boolean result4 = str.matches(regex2);
        // 判断是否为纯中文，不是返回false
        String regex3 = "[\\u4e00-\\u9fa5]+";
        boolean result5 = str.matches(regex3);
        System.out.println(result1 + "--" + result2 + "--" + result3
                + "--" + result4 + "--" + result5 + "--" + result6);
    }

    /**
     * 判断字符串是否为全中文
     * <p>
     * 利用正则表达式来判   1  不是   2 是
     *
     * @param str 被校验的字符串
     * @return 1  不是   2 是
     */
    public static int checkIsChinese(String str) {
        String regex3 = "[\\u4e00-\\u9fa5]+";
        if (!str.matches(regex3)) {
            return 1;
        }
        return 2;
    }

    public static int isThereNumber(String str) {
        String regex2 = ".*[0-9].*";
        if (!str.matches(regex2)) {
            return 3;
        }
        return 4;
    }

    /**
     * 验证是否全是数字
     *
     * @param str 被校验的字符串
     * @return true代表全是数字
     */
    public static boolean checkStrIsNum02(String str) {
        Matcher isNum = NUMBER_PATTERN.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     * 处理 createTime 和 updateTime
     *
     * @param recordList 要处理的数据
     * @return 处理后的数据
     */
    public static List<Record> handleTime(List<Record> recordList) {
        if (ObjectUtil.isNotEmpty(recordList)) {
            recordList.forEach(record -> {
                if (ObjectUtil.isNotEmpty(record)) {
                    handleTime(record);
                }
            });
        }
        return recordList;
    }

    /**
     * 处理 createTime 和 updateTime
     *
     * @param record 要处理的数据
     * @return 处理后的数据
     */
    public static Record handleTime(Record record) {
        if (ObjectUtil.isNotEmpty(record)) {
            try {
                // 处理时间
                if (ObjectUtil.isNotEmpty(record.getModel("createTime", Date.class))) {
                    record.set("createTime", DateUtil.formatDateTime(record.getModel("createTime", Date.class)));
                }
                if (ObjectUtil.isNotEmpty(record.getModel("updateTime", Date.class))) {
                    record.set("updateTime", DateUtil.formatDateTime(record.getModel("updateTime", Date.class)));
                }
            } catch (Exception ignored) {
                // 如果获取时间报错了 说明在上一步骤中曾经处理过 那么这里就不需要管了
            }
        }
        return record;
    }

}
